﻿// --------------------------------
// <copyright file="ActionsController.cs" company="Faculty of Information Technology, Czech Technical University in Prague">
//     This document is shared by Microsoft Public License (MS-PL)
//     For further details please see http://msdn.microsoft.com/en-us/library/ff648068.aspx or license.txt.
// </copyright>
// <author>Vladislav Richter</author>

// <email>richtvl2@fit.cvut.cz</email>

// <date>9. 11. 2011 22:22:37</date>

using ExternalClasses.Animations;
using FarseerPhysics.Collision.Shapes;
using FarseerPhysics.Dynamics.Contacts;
using FITClub.Episode1.Components;
using FITClub.Episode1.Controller;
using FITClub.Episode1.Levels;

// <summary>
//
// </summary>
// --------------------------------
using Microsoft.Xna.Framework;

namespace FITClub.Episode1.Controller
{
    /// <summary>
    /// Vitally important class which controls all of its avatars reactions and animations to player's input and outside stimuly
    /// </summary>
    public class ActionsController : IActionsController
    {
        #region FIELDS

        public PlayableCharacter Character;

        public ActionsStatus Status { get; private set; }

        private GameFITClub game;
        private MultiAnimationPlayer AnimationPlayer;
        private APlayer player;
        private VirtualGamepadState inputState;

        public double PowerUpTime { get; set; }

        #endregion FIELDS

        #region METHODS

        public ActionsController(PlayableCharacter character, APlayer player)
        {
            this.Character = character;
            this.Character.Respawned += new System.Action<PlayableCharacter>(Character_Respawned);
            this.AnimationPlayer = this.Character.AnimPlayer;
            this.player = player;
            game = character.game;

            Status = new ActionsStatus();
            PowerUpTime = 11;
        }

        private void Character_Respawned(PlayableCharacter pChar)
        {
            this.ResetEffects();

            this.Status.Stunned = false;
            this.Status.Movement = MovementStatus.Still;

            this.Status.StunnedTime = 0;
            pChar.AnimPlayer.StopTake("Get_Hit_Bottom");
            pChar.AnimPlayer.StartTake("Idle");
            this.Status.PowerUpEffect = PowerUpType.None;
        }

        public void Update(GameTime time)
        {
            if (Status.PowerUpTime > 0) Status.PowerUpTime -= time.ElapsedGameTime.TotalSeconds;

            #region POWERUP WEARED OFF

            if (Status.PowerUpTime < 0)
            {
                ResetEffects();
                Status.PowerUpEffect = PowerUpType.None;
                //this.player.Update();
            }

            #endregion POWERUP WEARED OFF

            inputState = this.player.ControllerStateActual;

            #region PROCESS ALL POWERUPS
            ProcessControlSwitch();
            ProcessSuperSpeed();
            ProcessSlowSpeed();
            ProcessSuperJump();
            
            ProcessInvisibility();
            ProcessSuperPunch();
            Status.NewPowerUp = false;

            #endregion PROCESS ALL POWERUPS

            if (Status.Stunned == false)
            {
                #region PROCESS PLAYER'S INPUT

                ProcessIdle(time);
                ProcessDuck(time);
                ProcessMovement(time);
                ProcessJump(time);
                ProcessBlock(time);
                ProcessAttack(time);

                #endregion PROCESS PLAYER'S INPUT
            }
            else

            #region PROCESS STUNN

            {
                if (Status.Movement == MovementStatus.Normal && Status.Movement == MovementStatus.Fast) this.AnimationPlayer.StopTake("Run");
                Status.Movement = MovementStatus.Still;

                if (Status.StunnedTime > 0)
                {
                    if (Character.contacts.Count > 0) Status.StunnedTime -= time.ElapsedGameTime.TotalSeconds;
                    else Status.ResetStunnedTime();
                }

                if (Status.StunnedTime < 0)
                {
                    Status.StunnedTime = 0;

                    Status.Stunned = false;
                    Character.CharPhysx.Torso.Rotation = 0;
                    Character.CharPhysx.Torso.FixedRotation = true;
                    Character.CharPhysx.Torso.ResetDynamics();
                    Character.CharPhysx.StandUp();
                    Character.LastContactWith = null;
                    Character.CharPhysx.Torso.Awake = true;
                    this.AnimationPlayer.StopTake("Get_Hit_Bottom");
                    this.AnimationPlayer.StartTake("Idle");
                }
            }

            #endregion PROCESS STUNN
        }

        #region PROCESS POWERUPS

        public void ResetEffects()
        {
            Status.PowerUpTime = 0.0;
            Character.CharPhysx.SetNormalSpeed();
            Character.Fade = false;
            if (Status.Moving)
            {
                if (Character.Direction > 0) Character.CharPhysx.MoveLeft();
                else Character.CharPhysx.MoveRight();
                this.AnimationPlayer.StopTake("Walk");
                this.AnimationPlayer.StartTake("Run");
            }
            if (Status.Moving) Status.Movement = MovementStatus.Normal;
            //Console.WriteLine(Player.Name + " powerup reset");
        }

        public void ProcessSuperSpeed()
        {
            if (Status.PowerUpEffect == PowerUpType.SuperSpeed)
            {
                if (Status.NewPowerUp)
                {
                    ResetEffects();
                    Status.PowerUpTime = this.PowerUpTime;
                    Status.NewPowerUp = false;
                    Character.CharPhysx.SetFastSpeed();
                    Character.PopUp.SetText("Super Speed", Color.Green);
                    if (Status.Moving)
                    {
                        Status.Movement = MovementStatus.Fast;
                        this.AnimationPlayer.StartTake("Run");
                    }
                }
            }
        }

        public void ProcessSlowSpeed()
        {
            if (Status.PowerUpEffect == PowerUpType.SlowSpeed)
            {
                if (Status.NewPowerUp)
                {
                    ResetEffects();
                    Status.PowerUpTime = this.PowerUpTime;
                    Status.NewPowerUp = false;
                    Character.CharPhysx.SetSlowSpeed();
                    Character.PopUp.SetText("Slow Speed", Color.Red);
                    if (Status.Moving)
                    {
                        Status.Movement = MovementStatus.Slow;
                        this.AnimationPlayer.StartTake("Walk");
                    }
                }
            }
        }

        public void ProcessSuperJump()
        {
            if (Status.PowerUpEffect == PowerUpType.SuperJump)
            {
                if (Status.NewPowerUp)
                {
                    ResetEffects();
                    Status.PowerUpTime = this.PowerUpTime;
                    Status.NewPowerUp = false;
                    Character.PopUp.SetText("Super Jump", Color.Green);
                }
            }
        }

        public void ProcessControlSwitch()
        {
            if (Status.PowerUpEffect == PowerUpType.ControlSwitch)
            {
                if (Status.NewPowerUp)
                {
                    ResetEffects();
                    Status.PowerUpTime = this.PowerUpTime;
                    Status.NewPowerUp = false;

                    //this.player.Update();
                    Character.PopUp.SetText("Control Switch", Color.Red);
                }
                this.inputState = this.player.ControllerStateActual.Copy();
                if (this.player.ControllerStateActual[(int)VirtualGamepadButtons.Down])
                {
                    this.inputState[(int)VirtualGamepadButtons.Up] = true;
                    this.inputState[(int)VirtualGamepadButtons.Down] = false;
                }
                if (this.player.ControllerStateActual[(int)VirtualGamepadButtons.Up])
                {
                    this.inputState[(int)VirtualGamepadButtons.Down] = true;
                    this.inputState[(int)VirtualGamepadButtons.Up] = false;
                }
                if (this.player.ControllerStateActual[(int)VirtualGamepadButtons.Left])
                {
                    this.inputState[(int)VirtualGamepadButtons.Right] = true;
                    this.inputState[(int)VirtualGamepadButtons.Left] = false;
                }
                if (this.player.ControllerStateActual[(int)VirtualGamepadButtons.Right])
                {
                    this.inputState[(int)VirtualGamepadButtons.Left] = true;
                    this.inputState[(int)VirtualGamepadButtons.Right] = false;
                }
            }
        }

        public void ProcessInvisibility()
        {
            if (Status.PowerUpEffect == PowerUpType.Invisibility)
            {
                if (Status.NewPowerUp)
                {
                    ResetEffects();
                    Status.PowerUpTime = this.PowerUpTime;
                    Status.NewPowerUp = false;
                    Character.Fade = true;
                    Character.PopUp.SetText("", Color.Black, 5.0);
                }
            }
        }

        public void ProcessSuperPunch()
        {
            if (Status.PowerUpEffect == PowerUpType.SuperPunch)
            {
                if (Status.NewPowerUp)
                {
                    ResetEffects();
                    Status.PowerUpTime = this.PowerUpTime;
                    Status.NewPowerUp = false;
                    Character.PopUp.SetText("Super Punch", Color.Green);
                }
            }
        }

        #endregion PROCESS POWERUPS

        #region PROCESS INPUT

        public void ProcessIdle(GameTime time)
        {
            if (Status.Idle == false)
            {
                this.AnimationPlayer.StartTake("Idle");
                Status.Idle = true;
            }
        }

        public void ProcessDuck(GameTime time)
        {
            if (this.inputState[(int)VirtualGamepadButtons.Down])
            {
                if (Status.Movement != MovementStatus.Duck)//(Status.Standing && Status.InCrouch == false)
                {
                    Status.Movement = MovementStatus.Duck;
                    Status.Moving = false;
                    Character.CharPhysx.Duck();
                    Character.CharPhysx.MotorStop();
                    this.AnimationPlayer.StopTake("Run");
                    this.AnimationPlayer.StopTake("Walk");
                    this.AnimationPlayer.StopTake("Idle");
                    if (Status.InBlock == false && Status.Blocking == false) this.AnimationPlayer.StartTake("Idle_upper");
                    if (this.AnimationPlayer.IsTakeRunning("Crouch") == false) this.AnimationPlayer.StartTake("Crouch", true);
                }
                else if (Status.Movement == MovementStatus.Duck && Status.InCrouch == false && this.AnimationPlayer.IsTakeRunning("Crouch") == false)
                {
                    Status.InCrouch = true;
                    this.AnimationPlayer.StartTake("Crouch_in");
                }
            }
            else
            {
                if (Status.Movement == MovementStatus.Duck)
                {
                    Status.Movement = MovementStatus.Still;
                    Status.InCrouch = false;
                    Character.CharPhysx.StandUp();
                    this.AnimationPlayer.StopTake("Idle_upper");
                    if (Status.InBlock == false && Status.Blocking == false) this.AnimationPlayer.StartTake("Idle");
                    else this.AnimationPlayer.StartTake("Idle_lower");
                    this.AnimationPlayer.StartTake("Crouch_end", true);
                }
            }
        }

        public void ProcessMovement(GameTime time)
        {
            if (this.inputState[(int)VirtualGamepadButtons.Left] || this.inputState[(int)VirtualGamepadButtons.Right])
            {
                if (Status.Moving)
                {
                    if (this.inputState[(int)VirtualGamepadButtons.Left]) Character.CharPhysx.MoveLeft();
                    else Character.CharPhysx.MoveRight();
                }
                else
                {
                    if (Status.Movement == MovementStatus.Jump)
                    {
                        if (this.inputState[(int)VirtualGamepadButtons.Left]) Character.CharPhysx.AirMovementLeft();
                        else Character.CharPhysx.AirMovementRight();
                    }
                    else if (Status.Moving == false && Status.Movement == MovementStatus.Still)
                    {
                        if (this.inputState[(int)VirtualGamepadButtons.Left]) Character.CharPhysx.MoveLeft();
                        else Character.CharPhysx.MoveRight();
                        if (Status.PowerUpEffect == PowerUpType.SlowSpeed)
                        {
                            Status.Movement = MovementStatus.Slow;
                            //Console.WriteLine("Set to Slow speed");
                        }
                        else
                        {
                            if (Status.PowerUpEffect == PowerUpType.SuperSpeed)
                            {
                                Status.Movement = MovementStatus.Fast;
                            }
                            else Status.Movement = MovementStatus.Normal;
                        }
                        Status.Moving = true;
                        if (Status.Movement == MovementStatus.Normal) this.AnimationPlayer.StartTake("Run");
                        if (Status.Movement == MovementStatus.Fast) this.AnimationPlayer.StartTake("Run");
                        if (Status.Movement == MovementStatus.Slow)
                        {
                            this.AnimationPlayer.StartTake("Walk");
                            //Console.WriteLine("started Walk");
                        }
                    }
                    else if (Status.Movement == MovementStatus.Duck)
                    {
                        if (this.inputState[(int)VirtualGamepadButtons.Left]) Character.Direction = +1;
                        else Character.Direction = -1;
                    }
                }
            }
            else
            {
                if (Status.Moving)
                {
                    Character.CharPhysx.MotorStop();
                    Status.Moving = false;
                    if (Status.Movement == MovementStatus.Normal || Status.Movement == MovementStatus.Fast || Status.Movement == MovementStatus.Slow) Status.Movement = MovementStatus.Still;
                    this.AnimationPlayer.StartTake("Idle");
                    this.AnimationPlayer.StopTake("Run");
                    this.AnimationPlayer.StopTake("Walk");
                }
            }
        }

        public void ProcessJump(GameTime time)
        {
            if (Status.Movement == MovementStatus.Jump)
            {
                if (WithinJumpAngle())
                {
                    Status.Movement = MovementStatus.Still;
                    Status.Flying = false;
                    Character.CharPhysx.StandUp();
                    game.SoundPlayer.PlaySoundEffect("Impact", Character.Position);
                    Character.CharPhysx.TinyJump();
                    this.AnimationPlayer.StartTake("Jump_end", true);
                    //Player.AnimPlayer.StopTake("Jump_in");
                    //Player.AnimPlayer.StartTake("Idle");
                }
                else
                {
                    if (this.AnimationPlayer.IsTakeRunning("Jump") == false && Status.Flying == false)
                    {
                        Status.Flying = true;
                        Character.CharPhysx.Duck();
                        this.AnimationPlayer.StartTake("Jump_in");
                    }
                }

                //if (Player.AnimPlayer.IsTakeRunning("Jump") == false)
                //{
                //    Status.Running = false;
                //}
            }
            else
            {
                if (Character.CharPhysx.Motor.Contacts.Count == 0 && Status.MiniJumping == false && Character.CharPhysx.Torso.LinearVelocity.Y < -3) Status.Freefalling = true;
                if (Status.Freefalling && Status.MiniJumping == false && Character.CharPhysx.Motor.Contacts.Count > 0)
                {
                    Status.Freefalling = false;
                    Status.MiniJumping = true;
                    game.SoundPlayer.PlaySoundEffect("Impact", Character.Position);
                    Character.CharPhysx.TinyJump();
                    if (!this.inputState[(int)VirtualGamepadButtons.Down]) this.AnimationPlayer.StartTake("Jump_end", true);
                }
                if (Status.MiniJumping && Character.CharPhysx.Motor.Contacts.Count > 0)
                {
                    Status.Freefalling = false;
                    Status.MiniJumping = false;
                }
                if (Status.TimeToNextJump != 0)
                {
                    Status.TimeToNextJump -= time.ElapsedGameTime.TotalSeconds;
                    if (Status.TimeToNextJump < 0) Status.TimeToNextJump = 0;
                }
                if (!this.inputState[(int)VirtualGamepadButtons.Up])
                {
                    Status.WaitingForJumpRelease = false;
                }
                else
                {
                    if (Status.WaitingForJumpRelease == false && Status.TimeToNextJump == 0 && WithinJumpAngle())
                    {
                        Status.WaitingForJumpRelease = true;
                        Status.Movement = MovementStatus.Jump;
                        Status.Flying = false;
                        Status.Moving = false;
                        Character.CharPhysx.Duck();
                        Status.ResetTimeToNextJump();
                        this.AnimationPlayer.StopTake("Run");
                        this.AnimationPlayer.StopTake("Walk");
                        this.AnimationPlayer.StartTake("Jump", true);
                        if (Status.PowerUpEffect != PowerUpType.SuperJump) Character.CharPhysx.Jump();
                        else Character.CharPhysx.SuperJump();
                        Character.CharPhysx.Motor.Contacts.Clear();
                    }
                }
            }
        }

        public void ProcessBlock(GameTime time)
        {
            if (this.inputState[(int)VirtualGamepadButtons.Defence])
            {
                if (Status.Blocking == false && Status.InBlock == false && this.inputState[(int)VirtualGamepadButtons.Attack] == false)
                {
                    Status.Blocking = true;
                    this.AnimationPlayer.StopTake("Idle");
                    if (Status.Movement != MovementStatus.Duck) this.AnimationPlayer.StartTake("Idle_lower");
                    this.AnimationPlayer.StartTake("Block", true);
                }
                if (Status.InBlock == false && this.inputState[(int)VirtualGamepadButtons.Attack] == false)
                {
                    if (this.AnimationPlayer.IsTakeRunning("Block") == false)
                    {
                        Status.InBlock = true;
                        this.AnimationPlayer.StartTake("Block_in");
                    }
                }
            }
            else
            {
                if (Status.Blocking)
                {
                    Status.Blocking = false;
                    Status.InBlock = false;
                    //Status.BlockReleaseDebaunce = 0;
                    //Player.AnimPlayer.StopTake("Block");
                    this.AnimationPlayer.StopTake("Idle_lower");
                    if (Status.Movement != MovementStatus.Duck) this.AnimationPlayer.StartTake("Idle");
                    else this.AnimationPlayer.StartTake("Idle_upper");
                    this.AnimationPlayer.StartTake("Block_end", true);
                }
                //else if (Status.Blocking) Status.BlockReleaseDebaunce++;
            }
        }

        public void ProcessAttack(GameTime time)
        {
            if (this.inputState[(int)VirtualGamepadButtons.Attack] == false && this.inputState[(int)VirtualGamepadButtons.Defence] == false)
            {
                Status.WaitingForAttackRelease = false;
            }
            if (Status.Attacking)
            {
                float progress;
                if (Status.AttackRightHand) progress = this.AnimationPlayer.GetTakeProgress("Punch");
                else progress = this.AnimationPlayer.GetTakeProgress("Double_Punch");
                if (progress > 0.5f)
                {
                    Character.CharPhysx.RFist.Sensor.Enabled = false;
                    Character.CharPhysx.LFist.Sensor.Enabled = false;
                    Status.TimeToAttackWithOherHand -= time.ElapsedGameTime.TotalSeconds;
                    if (Status.TimeToAttackWithOherHand < 0)
                    {
                        Status.TimeToAttackWithOherHand = 0;
                        Status.AttackRightHand = true;
                        Status.Attacking = false;
                    }
                }

                if (this.inputState[(int)VirtualGamepadButtons.Attack] && this.inputState[(int)VirtualGamepadButtons.Defence] == false && Status.WaitingForAttackRelease == false && Status.TimeToAttackWithOherHand > 0)
                {
                    if (Status.AttackRightHand)
                    {
                        Status.AttackRightHand = false;
                        this.AnimationPlayer.StartTake("Punch", true);
                        Character.CharPhysx.RFist.Sensor.Enabled = true;
                    }
                    else
                    {
                        Status.AttackRightHand = true;
                        this.AnimationPlayer.StartTake("Double_Punch", true);
                        Character.CharPhysx.LFist.Sensor.Enabled = true;
                    }
                    Status.ResetTimeToAttackWithOherHand();
                    Status.Attacking = true;
                    Status.WaitingForAttackRelease = true;
                }
            }
            else
            {
                if (this.inputState[(int)VirtualGamepadButtons.Attack] && this.inputState[(int)VirtualGamepadButtons.Defence] == false && Status.WaitingForAttackRelease == false)
                {
                    Status.Attacking = true;
                    Status.AttackRightHand = false;
                    Status.WaitingForAttackRelease = true;
                    Status.ResetTimeToAttackWithOherHand();
                    this.AnimationPlayer.StartTake("Punch", true);
                    Character.CharPhysx.RFist.Sensor.Enabled = true;
                }
            }
        }

        public bool WithinJumpAngle()
        {
            foreach (Contact c in Character.CharPhysx.Motor.Contacts)
            {
                float cosangle;
                if (c.FixtureA == null || c.FixtureB == null) continue;
                if (c.FixtureA.Shape.ShapeType == ShapeType.Circle && c.FixtureB.Shape.ShapeType == ShapeType.Circle)
                {
                    if (c.FixtureA.Body == Character.CharPhysx.Torso || c.FixtureA.Body == Character.CharPhysx.Motor.MotorWheel)
                    {
                        Vector2 normal = c.FixtureB.Body.Position - c.FixtureA.Body.Position;
                        normal.Normalize();
                        cosangle = Vector2.Dot(normal, new Vector2(0, -1));
                    }
                    else
                    {
                        Vector2 normal = c.FixtureA.Body.Position - c.FixtureB.Body.Position;
                        normal.Normalize();
                        cosangle = Vector2.Dot(normal, new Vector2(0, -1));
                    }
                }
                else cosangle = Vector2.Dot(c.Manifold.LocalNormal, new Vector2(0, 1));
                if (cosangle <= 1.0f && cosangle >= 0.8f)
                {
                    return true;
                }
            }
            return false;
        }

        #endregion PROCESS INPUT

        #endregion METHODS

        public void SetPowerUp(PowerUpType type)
        {
            this.Status.PowerUpEffect = type;
            this.Status.NewPowerUp = true;
        }
    }

    #region TEMP DATA STORAGE

    public enum MovementStatus { Still, Slow, Normal, Fast, Jump, Duck };

    public class ActionsStatus
    {
        //public bool Standing, Running,Attacking, AttackStarting, AttackEnding, AttackRightHand, Jumping, Blocking,
        //    Stunned, Idle, WaitingForJumpRelease, WaitingForAttackRelease, Flying, InBlock, InCrouch;
        public bool Attacking, Moving, AttackStarting, AttackEnding, AttackRightHand, Blocking,
            Stunned, Idle, WaitingForJumpRelease, WaitingForAttackRelease, Flying, InBlock, InCrouch, MiniJumping, Freefalling;
        public MovementStatus Movement;
        public double TimeToAttackWithOherHand, TimeToNextJump, StunnedTime;
        public float AttackProgress;
        public int BlockReleaseDebaunce;
        public PowerUpType PowerUpEffect = PowerUpType.None;
        public double PowerUpTime = 0.0;
        public int MovementReleaseDebounce = 0;
        public bool NewPowerUp = false;

        public ActionsStatus()
        {
            Movement = MovementStatus.Still;
            Moving = false;
            AttackRightHand = true;
        }

        public void ResetTimeToAttackWithOherHand() { TimeToAttackWithOherHand = 0.5; }

        public void ResetTimeToNextJump() { TimeToNextJump = 0.3; }

        public void ResetStunnedTime() { StunnedTime = 1.5; }
    }

    #endregion TEMP DATA STORAGE
}